/**
 * @author Fang Lu, fanglupku@gmail.com
 * 2011-4-7
 */
package persist;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.StringReader;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Properties;

import code.extract.CEConfig;
import code.extract.FileDealer;

import com.sleepycat.je.Cursor;
import com.sleepycat.je.Database;
import com.sleepycat.je.DatabaseConfig;
import com.sleepycat.je.DatabaseEntry;
import com.sleepycat.je.DatabaseException;
import com.sleepycat.je.Durability;
import com.sleepycat.je.Environment;
import com.sleepycat.je.EnvironmentConfig;
import com.sleepycat.je.LockMode;
import com.sleepycat.je.OperationStatus;
import com.sleepycat.je.Transaction;

public class APIDB {
	private Environment env;
	private Database api_id_db;
	private Database id_url_db;
	private long cacheSize = 1024 * 1024;
	private final String API_DBName = "api_id";
	private final String URL_DBName = "id_url";
	private String encode = "utf8";
	
	public static void main(String args[]){
		APIDB db = new APIDB(CEConfig.getAPIBDBDirname(), 1024 * 1024, "utf8");
		
		//init part, only run once
//		db.importAPI(CEConfig.getGoogleKeywordListFilename());
//		System.out.println("api import ok");
//		db.importLink(CEConfig.getGoogleLinklistFilename());
//		System.out.println("link import ok");
		
		//test part
		System.out.println(db.getLinkListByID("4001").size());
		System.out.println(db.getAllLinkList().size());
		for (int i=0; i<10; ++i)
			System.out.println(db.getAllLinkList().get(i));
//		for (int i=0; i<db.getAllLinkList().size(); ++i){
////			System.out.println(db.getAllLinkList().get(i));
//		}
		db.close();
	}
	
	public APIDB(String path, long cacheSize, String encode) {
		EnvironmentConfig envConfig = new EnvironmentConfig();
		envConfig.setAllowCreate(true);
		envConfig.setCacheSize(cacheSize);
//		envConfig.setTransactional(true);
//		envConfig.setDurability(Durability.COMMIT_SYNC);    
		
		try {
			File path_file = new File(path);
			if(!path_file.exists()){
				path_file.mkdirs();
			}
			env = new Environment(path_file, envConfig);
			this.setCacheSize(cacheSize);
			if (encode == null) {
				Properties initProp = new Properties(System.getProperties());
				this.setEncode(initProp.getProperty("file.encoding"));
			} else {
				this.setEncode(encode);
			}
			DatabaseConfig dbConfig = new DatabaseConfig();
			dbConfig.setAllowCreate(true);
			dbConfig.setTransactional(false);
			try {
				api_id_db = env.openDatabase(null, API_DBName, dbConfig);
				id_url_db = env.openDatabase(null, URL_DBName, dbConfig);
			} catch (DatabaseException e) {
				e.printStackTrace();
			}
		} catch (DatabaseException e) {
			e.printStackTrace();
		}
	}
	
	public ArrayList<String> getAllAPI(){
		ArrayList<String> result = new ArrayList<String>();
		HashSet<String> singleSet = new HashSet<String>();
		Cursor cursor = null;
		try {
		    cursor = api_id_db.openCursor(null, null);
		    DatabaseEntry foundKey = new DatabaseEntry();
		    DatabaseEntry foundData = new DatabaseEntry();

		    while (cursor.getPrev(foundKey, foundData, LockMode.DEFAULT)
		       == OperationStatus.SUCCESS) {
		        try {
					String theKey = new String(foundKey.getData(), encode);
//					String theData = new String(foundData.getData(), encode);
					if (!singleSet.contains(theKey)){
						result.add(theKey);
						singleSet.add(theKey);
					}
				} catch (UnsupportedEncodingException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				} catch (IOException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
		    }
		} catch (DatabaseException de) {
		    System.err.println("Error accessing database." + de);
		} finally {
		    cursor.close();
		}
		return result;
	}
	
	public ArrayList<String> getAllLinkList(){
		ArrayList<String> result = new ArrayList<String>();
		HashSet<String> singleSet = new HashSet<String>();
		Cursor cursor = null;
		try {
		    cursor = id_url_db.openCursor(null, null);
		    DatabaseEntry foundKey = new DatabaseEntry();
		    DatabaseEntry foundData = new DatabaseEntry();

		    while (cursor.getPrev(foundKey, foundData, LockMode.DEFAULT)
		       == OperationStatus.SUCCESS) {
		        try {
//					String theKey = new String(foundKey.getData(), encode);
					String theData = new String(foundData.getData(), encode);
					StringReader sr = new StringReader(theData);
					BufferedReader br = new BufferedReader(sr);
					String oneLine = br.readLine();
					while (oneLine != null){
						oneLine = oneLine.trim();
						if (oneLine.length() > 0 && !singleSet.contains(oneLine)){
							result.add(oneLine);
							singleSet.add(oneLine);
						}
						oneLine = br.readLine();
					}
					br.close();
					sr.close();
				} catch (UnsupportedEncodingException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				} catch (IOException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
		    }
		} catch (DatabaseException de) {
		    System.err.println("Error accessing database." + de);
		} finally {
		    cursor.close();
		}
		return result;
	}
	
	public HashMap<Integer, String> getIDMapAPI(){
		HashMap<Integer, String> result = new HashMap<Integer, String>();
		Cursor cursor = null;
		try {
		    cursor = api_id_db.openCursor(null, null);
		    DatabaseEntry foundKey = new DatabaseEntry();
		    DatabaseEntry foundData = new DatabaseEntry();

		    while (cursor.getPrev(foundKey, foundData, LockMode.DEFAULT)
		       == OperationStatus.SUCCESS) {
		        try {
					String theKey = new String(foundKey.getData(), encode);
					String theData = new String(foundData.getData(), encode);
					result.put(Integer.parseInt(theData), theKey);
				} catch (UnsupportedEncodingException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				} catch (IOException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
		    }
		} catch (DatabaseException de) {
		    System.err.println("Error accessing database." + de);
		} finally {
		    cursor.close();
		}
		return result;
	}
	
	public HashMap<String, String> getLinkMapAPI(){
		HashMap<String, String> result = new HashMap<String, String>();
		HashMap<Integer, String> IDMapAPI = getIDMapAPI();
		Cursor cursor = null;
		try {
		    cursor = id_url_db.openCursor(null, null);
		    DatabaseEntry foundKey = new DatabaseEntry();
		    DatabaseEntry foundData = new DatabaseEntry();
		    int count = 0;

		    while (cursor.getPrev(foundKey, foundData, LockMode.DEFAULT)
		       == OperationStatus.SUCCESS) {
		        try {
					String theKey = new String(foundKey.getData(), encode);
					String theName = IDMapAPI.get(Integer.parseInt(theKey));
//					System.out.println(theName);
					String theData = new String(foundData.getData(), encode);
					StringReader sr = new StringReader(theData);
					BufferedReader br = new BufferedReader(sr);
					String oneLine = br.readLine();
					while (oneLine != null){
						oneLine = oneLine.trim();
						if (oneLine.length() > 0 ){
							result.put(oneLine, theName);
						}
						oneLine = br.readLine();
					}
					br.close();
					sr.close();
					++ count;
					System.out.println(count);
				} catch (UnsupportedEncodingException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				} catch (IOException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
		    }
		} catch (DatabaseException de) {
		    System.err.println("Error accessing database." + de);
		} finally {
		    cursor.close();
		}
		return result;
	}
	
	private boolean putAPI_ID(String apiname, String id) throws UnsupportedEncodingException  {
		byte[] theKey = apiname.getBytes(encode);
		byte[] theValue = id.getBytes(encode);
//		Transaction txn = env.beginTransaction(null, null);
		OperationStatus status = api_id_db.put(null, new DatabaseEntry(theKey),
				new DatabaseEntry(theValue));
//		txn.commit();
		if (status == OperationStatus.SUCCESS) {
			return true;
		}
		return false;
	}
	
	private boolean putID_URL(String id, String url) throws UnsupportedEncodingException  {
		byte[] theKey = id.getBytes(encode);
		byte[] theValue = url.getBytes(encode);
//		Transaction txn = env.beginTransaction(null, null);
		OperationStatus status = id_url_db.put(null, new DatabaseEntry(theKey),
				new DatabaseEntry(theValue));
//		txn.commit();
		if (status == OperationStatus.SUCCESS) {
			return true;
		}
		return false;
	}
	
	public String getIDOfAPI(String apiname){
		DatabaseEntry queryKey = new DatabaseEntry();
		DatabaseEntry value = new DatabaseEntry();
		try {
			queryKey.setData(apiname.getBytes(encode));
		} catch (UnsupportedEncodingException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
			return null;
		}
		OperationStatus status = api_id_db
				.get(null, queryKey, value, LockMode.DEFAULT);
		if (status == OperationStatus.SUCCESS) {
			return new String(value.getData());
		}
		return null;
	}
	
	public ArrayList<String> getLinkListByID(String id){
		ArrayList<String> result = new ArrayList<String>();
		
		DatabaseEntry queryKey = new DatabaseEntry();
		DatabaseEntry value = new DatabaseEntry();
		try {
			queryKey.setData(id.getBytes(encode));
		} catch (UnsupportedEncodingException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
			return result;
		}
		OperationStatus status = id_url_db
				.get(null, queryKey, value, LockMode.DEFAULT);
		if (status == OperationStatus.SUCCESS) {
			String temp = new String(value.getData());
			StringReader sr = new StringReader(temp);
			BufferedReader br = new BufferedReader(sr);
			String oneLine;
			try {
				oneLine = br.readLine();
				while(oneLine != null){
//					System.out.println(oneLine);
					result.add(oneLine);
					oneLine = br.readLine();
				}
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			} finally{
				try {
					br.close();
				} catch (IOException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
				sr.close();
			}
			
		}
		
		return result;
	}
	
	public ArrayList<String> getLinkListByAPIName(String apiname){
		String id = getIDOfAPI(apiname);
		return getLinkListByID(id);
	}
	
	
	public boolean importAPI(String apiListFileName){
		long index = 0;
		try {
			File file = new File(apiListFileName);
			FileInputStream fis;
			InputStreamReader isr;
			BufferedReader br;
			String oneLine;
			fis = new FileInputStream(file);
			if (encode == null){
				isr = new InputStreamReader(fis);
			}else{
				isr = new InputStreamReader(fis, encode);
			}
			br = new BufferedReader(isr);
			oneLine = br.readLine();
			while (oneLine != null) {
				String tempLong = "" + index;
				if (putAPI_ID(oneLine, tempLong) == true){
					++ index;
				}
				oneLine = br.readLine();
			}
			br.close();
			isr.close();
			fis.close();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
			return false;
		}
		return true;
	}
	
	public boolean importLink(String linkListFileName){
		try {
			File file = new File(linkListFileName);
			FileInputStream fis;
			InputStreamReader isr;
			BufferedReader br;
			String oneLine;
			fis = new FileInputStream(file);
			if (encode == null){
				isr = new InputStreamReader(fis);
			}else{
				isr = new InputStreamReader(fis, encode);
			}
			br = new BufferedReader(isr);
			oneLine = br.readLine();
			String thisID = "-1";
			String thisAPI = "";
			HashSet<String> tempSet = new HashSet<String>();
			ArrayList<String> tempList = new ArrayList<String>(); 
			while (oneLine != null) {
				if (oneLine.startsWith("#") == true){
					if (!thisID.equals("-1")){
						StringBuffer sb = new StringBuffer();
						for (int i=0; i<tempList.size(); ++i){
							sb.append(tempList.get(i));
							sb.append("\n");
						}
						boolean ret = putID_URL(thisID, sb.toString());
						System.out.println(ret);
						tempList.clear();
						tempSet.clear();
					}
					System.out.println("id " + thisID + " is ok!");
					thisAPI = new String(oneLine.substring(1));
					thisID = getIDOfAPI(thisAPI);
				}else{
					if (!tempSet.contains(oneLine)){
						tempList.add(oneLine);
						tempSet.add(oneLine);
					}
				}
				oneLine = br.readLine();
			}
			br.close();
			isr.close();
			fis.close();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
			return false;
		}
		return true;
	}
	
	public void close() {
		try {
			if (api_id_db != null) {
				api_id_db.close();
			}
			if (id_url_db != null) {
				id_url_db.close();
			}
			if (env != null) {
				env.cleanLog();
				env.close();
			}
		} catch (DatabaseException e) {
			e.printStackTrace();
		}
	}

	public void setEncode(String encode) {
		this.encode = encode;
	}

	public String getEncode() {
		return encode;
	}

	public void setCacheSize(long cacheSize) {
		this.cacheSize = cacheSize;
	}

	public long getCacheSize() {
		return cacheSize;
	}
}
